One feature that’s really nice about the Image element is that you can set the Source property to a URL, such as in this Silverlight project:
Example 1. Silverlight Project: SilverlightWebBitmap File: MainPage.xaml (excerpt)
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Image Source="http://www.charlespetzold.com/Media/HelloWP7.jpg" /> </Grid>
|
Here it is:
This is certainly easy enough, and pulling images off the
Web rather than binding them into the application certainly keeps the
size of the executable down. But an application running on Windows Phone
7 is not guaranteed to have an Internet connection, and you’re
undoubtedly associated with other problems associated with downloading. The Image element has two events named ImageOpened and ImageFailed that you can use to determine if the download was successful or not.
For Windows Phone 7 programs that display a lot of bitmaps,
you need to do some hard thinking. You can embed the bitmaps into the
executable and have their access guaranteed, or you can save space and
download them when necessary.
In XNA, downloading a bitmap from the Web is not quite as easy, but a .NET class named WebClient makes the job relatively painless. It’s somewhat easier to use than the common alternative (HttpWebRequest and HttpWebResponse) and is often the preferred choice for downloading individual items.
You can use WebClient to download either strings (commonly XML files) or binary objects. The actual transfer occurs asynchronously and then WebClient
calls a method in your program to indicate completion or failure. This
method call is in your program’s thread, so you get the benefit of an
asynchronous data transfer without explicitly dealing with secondary
threads.
To use WebClient in an XNA program, you’ll need to add a reference to the System.Net
library: In the Solution Explorer, under the project name, right click
References and select Add Reference. In the .NET table, select
System.Net. (Silverlight programs get a reference to System.Net
automatically.)
The Game1.cs file of the XnaWebBitmap project also requires a using directive for the System.Net namespace. The program defines the same fields as the earlier program:
Example 2. XNA Project: XnaWebBitmap File: Game1.cs (excerpt showing fields)
public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; SpriteBatch spriteBatch; Texture2D helloTexture; Vector2 position; . . . }
|
The LoadContent method creates an instance of WebClient, sets the callback method, and then initiates the transfer:
Example 3. XNA Project: XnaWebBitmap File: Game1.cs (excerpt)
protected override void LoadContent() { spriteBatch = new SpriteBatch(GraphicsDevice);
WebClient webClient = new WebClient(); webClient.OpenReadCompleted += OnWebClientOpenReadCompleted; webClient.OpenReadAsync(new Uri("http://www.charlespetzold.com/Media/HelloWP7. jpg")); }
|
The OnWebClientOpenReadCompleted
method is called when the entire file has been downloaded. You’ll want
to check if the download hasn’t been cancelled and that no error has
been reported. If everything is OK, the Result property of the event arguments is of type Stream. You can use that Stream with the static Texture2D.FromStream method to create a Texture2D object:
Example 4. XNA Project: XnaWebBitmap File: Game1.cs (excerpt)
void OnWebClientOpenReadCompleted(object sender, OpenReadCompletedEventArgs args) { if (!args.Cancelled && args.Error == null) { helloTexture = Texture2D.FromStream(this.GraphicsDevice, args.Result); Viewport viewport = this.GraphicsDevice.Viewport; position = new Vector2((viewport.Width - helloTexture.Width) / 2, (viewport.Height - helloTexture.Height) / 2); } }
|
The Texture2D.FromStream method supports JPEG, PNG, and GIF.
By default, the AllowReadStreamBuffering property of WebClient is true, which means that the entire file will have been downloaded when the OpenReadCompleted event is raised. The Stream object available in the Result property is actually a memory stream, except that it’s an instance of a class internal to the .NET libraries rather than MemoryStream itself.
If you set AllowReadStreamBuffering to false, then the Result property will be a network stream. The Texture2D class will not allow you to read from that stream on the main program thread.
Normally the LoadContent method of a Game derivative is called before the first call to the Update or Draw method, but it is essential to remember that a gap of time will separate LoadContent from the OnWebClientOpenReadCompleted method. During that time an asynchronous read is occurring, but the Game1 class is proceeding as normal with calls to Update and Draw. For that reason, you should only attempt to access the Texture2D object when you know that it’s valid:
Example 5. XNA Project: XnaWebBitmap File: Game1.cs (excerpt)
protected override void Draw(GameTime gameTime) { GraphicsDevice.Clear(Color.Navy);
if (helloTexture != null) { spriteBatch.Begin(); spriteBatch.Draw(helloTexture, position, Color.White); spriteBatch.End(); }
base.Draw(gameTime); }
|
In a real program, you’d also want to provide some kind of notification to the user if the bitmap could not be downloaded.